package Day_0225.SingletonPattern;

/**
 * @author zxc
 * @date 2023/02/25 23:19
 **/
public class SingletonPattern {
    public static void main(String[] args) {
        /**
         * 谈一谈对于 设计模式中单例模式的理解
         * ===》
         * 一，.单例模式的定义
         * ===》
         * 保证一个类只有一个实例，并且提供一个访问该全局访问点
         *
         * 二，单例模式的适用场景
         * 1. 网站的计数器，一般也是采用单例模式实现，否则难以同步。
         * 2. 应用程序的日志应用，一般都是单例模式实现，只有一个实例去操作才好，否则内容不好追加显示。
         * 3. 多线程的线程池的设计一般也是采用单例模式，因为线程池要方便对池中的线程进行控制
         * 4. Windows的（任务管理器）就是很典型的单例模式，他不能打开俩个
         * 5. windows的（回收站）也是典型的单例应用。在整个系统运行过程中，回收站只维护一个实例。
         *
         * 三，单例模式的优缺点
         * 1.优点：
         * （1）在单例模式中，活动的单例只有一个实例，对单例类的所有实例化得到的都是相同的一个实例。
         *  这样就防止其它对象对自己的实例化，确保所有的对象都访问一个实例
         *
         * （2）单例模式具有一定的伸缩性，类自己来控制实例化进程，类就在改变实例化进程上有相应的伸缩性。
         *
         * （3）提供了对唯一实例的受控访问。
         * （4）由于在系统内存中只存在一个对象，因此可以节约系统资源，
         *  当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
         * （5）允许可变数目的实例。
         * （6）避免对共享资源的多重占用。
         *
         * 2.缺点：
         * （1）不适用于变化的对象，如果同一类型的对象总是要在不同的用例场景发生变化，
         *  单例就会引起数据的错误，不能保存彼此的状态。
         * （2）由于单利模式中没有抽象层，因此单例类的扩展有很大的困难。
         * （3）单例类的职责过重，在一定程度上违背了“单一职责原则”。
         *  ===》
         *  由于单例类中所有对象的创建都是 同一个单例类实例，所以所有创建出来的单例类实例都是共享数据的;
         *
         * （4）滥用单例将带来一些负面问题，
         *  a.如为了节省资源将数据库连接池对象设计为的单例类，可能会导致共享连接池对象的程序过多而出现连接池溢出；
         *  b.如果实例化的对象长时间不被利用，系统会认为是垃圾而被回收，这将导致对象状态的丢失。
         *
         * 四，单例模式使用注意事项：
         * 1. 使用时不能用反射模式创建单例，否则会实例化一个新的对象;
         * ===》即，单例类的实现就是 其构造方法是私有的，不能够通过调用构造方法，去实例化对象;
         * 2. 使用懒单例模式时注意线程安全问题;
         * 3. 饿单例模式和懒单例模式构造方法都是私有的，因而是不能被继承的，有些单例模式可以被继承;（如登记式模式）
         *
         * 五，单例的创建方式
         * 1. 饿汉式 :
         * ===》
         * （1）类初始化时,会立即加载该对象，线程天生安全,调用效率高。
         * （2）饿汉式的单例模式中，其构造方法是private的，是不对于外界公开的;
         *
         * 2. 懒汉式 :
         * ===》
         * （1）类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
         * （2）只有当类被使用时，才会创建单例对象 && getInstance()方法需要加上Synchronized关键字，保证同步机制;
         *  ===》
         *  若是未加上Synchronized关键字的话，在高并发情况下，可能会创建出多个类实例，不能够保证单例;
         *
         * 3. 静态内部方式 :
         * ===》
         * 结合了懒汉式和饿汉式各自的优点，真正需要对象的时候才会加载，加载类是线程安全的。
         *
         * 4. 枚举单例:
         * ===》
         * （1）使用枚举实现单例模式
         * （2）优点 ：实现简单、调用效率高，枚举本身就是单例，由jvm从根本上提供保障!避免通过反射和反序列化的漏洞，
         * （3）缺点 ：没有延迟加载。
         *
         * 5. 双重检测锁方式 (因为JVM本质重排序的原因，可能会初始化多次，不推荐使用)
         *
         * 六，如何选择单例创建方式
         * 1.如果不需要延迟加载单例，可以使用枚举或者饿汉式，相对来说枚举性好于饿汉式。
         * ===》
         * 即，若是不需要延迟加载单例对象的话，则选择饿汉式/枚举;
         *
         * 2.如果需要延迟加载，可以使用静态内部类或者懒汉式，相对来说静态内部类好于懒汉式。
         * ===》
         * （1）即，若是需要延迟加载单例对象的话，则选择懒汉式/静态内部类;
         * （2）静态内部类中，不需要加上Synchronized关键字，
         *  由于其使用静态内部类来加载实例对象（类似于懒汉式），来保证 在高并发情况下的线程安全;
         * （3）懒汉式中，需要对于获取实例对象的方法 加上Synchronized关键字/双重锁检验机制，来保证 在高并发情况下的线程安全;
         *
         */
    }
}
//饿汉式
class Demo1 {
    // 类初始化时,会立即加载该对象，线程安全,调用效率高
    private static Demo1 demo1 = new Demo1();
    private Demo1() {
        System.out.println("私有Demo1构造参数初始化");
    }
    // 由于永远只有一个对象实例 && 共享数据，在高并发情况下，创建该类的实例对象所获取皆为 初始化时加载的单例实例
    // ===》所以饿汉式必然是线程安全的;
    public static Demo1 getInstance() {
        return demo1;
    }
    public static void main(String[] args) {
        Demo1 s1 = Demo1.getInstance();
        Demo1 s2 = Demo1.getInstance();
        System.out.println(s1 == s2);
    }
}

//懒汉式
class Demo2 {
    //类初始化时，不会初始化该对象，真正需要使用的时候才会创建该对象。
    private static Demo2 demo2;
    private Demo2() {
        System.out.println("私有Demo2构造参数初始化");
    }
    // 所提供的（public）getInstance方法，加上Synchronized关键字，来保证高并发情况下，单例模式的可靠性;
    public synchronized static Demo2 getInstance() {
        if (demo2 == null) {
            demo2 = new Demo2();
        }
        return demo2;
    }
    public static void main(String[] args) {
        Demo2 s1 = Demo2.getInstance();
        Demo2 s2 = Demo2.getInstance();
        System.out.println(s1 == s2);
    }
}
// 静态内部类方式
class Demo3 {
    private Demo3() {
        System.out.println("私有Demo3构造参数初始化");
    }
    public static class SingletonClassInstance {
        private static final Demo3 DEMO_3 = new Demo3();
    }
    // 静态内部类的getInstance方法，并未进行同步处理，
    // 但是其所获取实例变量时，所调用的是 静态内部类中已经实例化过的对象;
    // ===》所以 在高并发情况下，获取静态内部类中已经定义好的类实例，永远都是单例实例;
    // ===》所以，静态内部类实现单例模式也不会出现线程安全问题;
    public static Demo3 getInstance() {
        return SingletonClassInstance.DEMO_3;
    }
    public static void main(String[] args) {
        Demo3 s1 = Demo3.getInstance();
        Demo3 s2 = Demo3.getInstance();
        System.out.println(s1 == s2);
    }
}
//使用枚举实现单例模式
// 优点 ：实现简单、枚举本身就是单例，由jvm从根本上提供保障!避免通过反射和反序列化的漏洞
// 缺点 ：没有延迟加载
class Demo4 {
    public static Demo4 getInstance() {
        return Demo.INSTANCE.getInstance();
    }
    public static void main(String[] args) {
        Demo4 s1 = Demo4.getInstance();
        Demo4 s2 = Demo4.getInstance();
        System.out.println(s1 == s2);
    }
    //定义枚举
    private static enum Demo {
        INSTANCE;
        // 枚举元素为单例
        private Demo4 demo4;
        private Demo() {
            System.out.println("枚举Demo私有构造参数");
            demo4 = new Demo4();
        }
        public Demo4 getInstance() {
            return demo4;
        }
    }
}
//双重检测锁方式
class Demo5 {
    private static Demo5 demo5;
    private Demo5() {
        System.out.println("私有Demo4构造参数初始化");
    }
    public static Demo5 getInstance() {
        // 即，懒汉式单例模式的升级
        // （除了Synchronized关键字外，使用双重锁机制，来保证单例类只会被创建一个实例;）
        if (demo5 == null) {
            synchronized (Demo5.class) {
                if (demo5 == null) {
                    demo5 = new Demo5();
                }
            }
        }
        return demo5;
    }
    public static void main(String[] args) {
        Demo5 s1 = Demo5.getInstance();
        Demo5 s2 = Demo5.getInstance();
        System.out.println(s1 == s2);
    }
}